#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
插件加载器模块
实现插件的自动发现、加载和管理

支持功能:
1. entry_points机制插件发现
2. 目录扫描插件发现
3. 插件依赖管理
4. 插件生命周期管理
5. 插件安全验证
"""

import os
import sys
import json
import time
import hashlib
import importlib
import importlib.util
import pkg_resources
from typing import Dict, List, Optional, Any, Type, Callable
from dataclasses import dataclass, field
import logging
import threading
import traceback
from pathlib import Path

from .base_plugin import BasePlugin, PluginMetadata, PluginConfig, PluginResult, PluginStatus, PluginType

@dataclass
class PluginEntry:
    """插件入口信息"""
    name: str
    module_name: str
    class_name: str
    file_path: str
    entry_point: Optional[str] = None
    checksum: str = ""
    loaded: bool = False
    instance: Optional[BasePlugin] = None

class PluginSecurity:
    """插件安全验证"""
    
    @staticmethod
    def calculate_file_hash(file_path: str) -> str:
        """计算文件哈希值"""
        try:
            with open(file_path, 'rb') as f:
                return hashlib.sha256(f.read()).hexdigest()
        except Exception:
            return ""
    
    @staticmethod
    def validate_plugin_code(file_path: str) -> bool:
        """验证插件代码安全性"""
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                content = f.read()
            
            # 检查危险操作
            dangerous_patterns = [
                'os.system(',
                'subprocess.call(',
                'eval(',
                'exec(',
                '__import__(',
                'open(',  # 需要更细致的检查
                'file(',
                'input(',
                'raw_input('
            ]
            
            # 简单的静态分析
            for pattern in dangerous_patterns:
                if pattern in content:
                    # 这里可以实现更复杂的上下文分析
                    logging.warning(f"Plugin {file_path} contains potentially dangerous operation: {pattern}")
            
            return True  # 暂时允许所有插件
            
        except Exception as e:
            logging.error(f"Failed to validate plugin {file_path}: {e}")
            return False

class PluginLoader:
    """插件加载器"""
    
    def __init__(self, plugins_dirs: List[str] = None, enable_entry_points: bool = True):
        """
        初始化插件加载器
        
        Args:
            plugins_dirs: 插件目录列表
            enable_entry_points: 是否启用entry_points机制
        """
        self.plugins_dirs = plugins_dirs or []
        self.enable_entry_points = enable_entry_points
        self.discovered_plugins = {}
        self.loaded_plugins = {}
        self.plugin_configs = {}
        self.logger = logging.getLogger("plugin_loader")
        
        # 默认插件目录
        default_dirs = [
            os.path.join(os.path.dirname(__file__), "plugins"),
            os.path.join(os.getcwd(), "plugins"),
            os.path.expanduser("~/.privilege_ma/plugins")
        ]
        
        for dir_path in default_dirs:
            if dir_path not in self.plugins_dirs:
                self.plugins_dirs.append(dir_path)
        
        # 确保插件目录存在
        for dir_path in self.plugins_dirs:
            os.makedirs(dir_path, exist_ok=True)
    
    def discover_plugins(self) -> Dict[str, PluginEntry]:
        """发现所有可用插件"""
        self.discovered_plugins.clear()
        
        # 1. 通过entry_points发现插件
        if self.enable_entry_points:
            self._discover_entry_point_plugins()
        
        # 2. 通过目录扫描发现插件
        self._discover_directory_plugins()
        
        self.logger.info(f"Discovered {len(self.discovered_plugins)} plugins")
        return self.discovered_plugins
    
    def _discover_entry_point_plugins(self):
        """通过entry_points发现插件"""
        try:
            for entry_point in pkg_resources.iter_entry_points('privilege_ma.plugins'):
                try:
                    plugin_entry = PluginEntry(
                        name=entry_point.name,
                        module_name=entry_point.module_name,
                        class_name=entry_point.attrs[0] if entry_point.attrs else "Plugin",
                        file_path="",  # entry_point插件可能没有本地文件
                        entry_point=str(entry_point)
                    )
                    
                    self.discovered_plugins[entry_point.name] = plugin_entry
                    self.logger.debug(f"Discovered entry_point plugin: {entry_point.name}")
                    
                except Exception as e:
                    self.logger.error(f"Failed to process entry_point {entry_point.name}: {e}")
                    
        except Exception as e:
            self.logger.error(f"Failed to discover entry_point plugins: {e}")
    
    def _discover_directory_plugins(self):
        """通过目录扫描发现插件"""
        for plugins_dir in self.plugins_dirs:
            if not os.path.exists(plugins_dir):
                continue
            
            self.logger.debug(f"Scanning plugin directory: {plugins_dir}")
            
            for item in os.listdir(plugins_dir):
                item_path = os.path.join(plugins_dir, item)
                
                # 扫描Python文件
                if item.endswith('.py') and not item.startswith('__'):
                    self._process_plugin_file(item_path, item[:-3])
                
                # 扫描插件包目录
                elif os.path.isdir(item_path):
                    init_file = os.path.join(item_path, '__init__.py')
                    if os.path.exists(init_file):
                        self._process_plugin_package(item_path, item)
    
    def _process_plugin_file(self, file_path: str, plugin_name: str):
        """处理单个插件文件"""
        try:
            # 安全验证
            if not PluginSecurity.validate_plugin_code(file_path):
                self.logger.warning(f"Plugin {plugin_name} failed security validation")
                return
            
            # 计算文件哈希
            checksum = PluginSecurity.calculate_file_hash(file_path)
            
            # 尝试找到插件类
            class_name = self._find_plugin_class(file_path)
            if not class_name:
                self.logger.debug(f"No plugin class found in {file_path}")
                return
            
            plugin_entry = PluginEntry(
                name=plugin_name,
                module_name=plugin_name,
                class_name=class_name,
                file_path=file_path,
                checksum=checksum
            )
            
            self.discovered_plugins[plugin_name] = plugin_entry
            self.logger.debug(f"Discovered file plugin: {plugin_name}")
            
        except Exception as e:
            self.logger.error(f"Failed to process plugin file {file_path}: {e}")
    
    def _process_plugin_package(self, package_path: str, plugin_name: str):
        """处理插件包目录"""
        try:
            init_file = os.path.join(package_path, '__init__.py')
            
            # 安全验证
            if not PluginSecurity.validate_plugin_code(init_file):
                self.logger.warning(f"Plugin package {plugin_name} failed security validation")
                return
            
            # 计算包哈希（所有Python文件）
            checksum = self._calculate_package_hash(package_path)
            
            # 查找插件类
            class_name = self._find_plugin_class(init_file)
            if not class_name:
                # 尝试在包的其他文件中查找
                for py_file in Path(package_path).glob("*.py"):
                    if py_file.name != '__init__.py':
                        class_name = self._find_plugin_class(str(py_file))
                        if class_name:
                            break
            
            if not class_name:
                self.logger.debug(f"No plugin class found in package {package_path}")
                return
            
            plugin_entry = PluginEntry(
                name=plugin_name,
                module_name=plugin_name,
                class_name=class_name,
                file_path=package_path,
                checksum=checksum
            )
            
            self.discovered_plugins[plugin_name] = plugin_entry
            self.logger.debug(f"Discovered package plugin: {plugin_name}")
            
        except Exception as e:
            self.logger.error(f"Failed to process plugin package {package_path}: {e}")
    
    def _find_plugin_class(self, file_path: str) -> Optional[str]:
        """在文件中查找插件类"""
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                content = f.read()
            
            # 简单的正则匹配查找继承BasePlugin的类
            import re
            pattern = r'class\s+(\w+)\s*\([^)]*BasePlugin[^)]*\):'
            matches = re.findall(pattern, content)
            
            if matches:
                return matches[0]  # 返回第一个匹配的类名
            
            return None
            
        except Exception as e:
            self.logger.error(f"Failed to find plugin class in {file_path}: {e}")
            return None
    
    def _calculate_package_hash(self, package_path: str) -> str:
        """计算包的哈希值"""
        try:
            hasher = hashlib.sha256()
            
            for py_file in sorted(Path(package_path).glob("*.py")):
                with open(py_file, 'rb') as f:
                    hasher.update(f.read())
            
            return hasher.hexdigest()
            
        except Exception:
            return ""
    
    def load_plugin(self, plugin_name: str, config: PluginConfig = None) -> bool:
        """
        加载指定插件
        
        Args:
            plugin_name: 插件名称
            config: 插件配置
            
        Returns:
            bool: 加载是否成功
        """
        if plugin_name not in self.discovered_plugins:
            self.logger.error(f"Plugin {plugin_name} not discovered")
            return False
        
        if plugin_name in self.loaded_plugins:
            self.logger.warning(f"Plugin {plugin_name} already loaded")
            return True
        
        plugin_entry = self.discovered_plugins[plugin_name]
        
        try:
            # 加载模块
            if plugin_entry.entry_point:
                # 通过entry_point加载
                module = self._load_entry_point_module(plugin_entry)
            else:
                # 通过文件路径加载
                module = self._load_file_module(plugin_entry)
            
            if not module:
                return False
            
            # 获取插件类
            plugin_class = getattr(module, plugin_entry.class_name, None)
            if not plugin_class:
                self.logger.error(f"Plugin class {plugin_entry.class_name} not found in module")
                return False
            
            # 验证插件类
            if not issubclass(plugin_class, BasePlugin):
                self.logger.error(f"Plugin class {plugin_entry.class_name} does not inherit from BasePlugin")
                return False
            
            # 创建插件实例
            plugin_instance = plugin_class(config or PluginConfig())
            
            # 检查兼容性
            if not plugin_instance.is_compatible():
                self.logger.error(f"Plugin {plugin_name} is not compatible with current environment")
                return False
            
            # 保存插件信息
            plugin_entry.instance = plugin_instance
            plugin_entry.loaded = True
            self.loaded_plugins[plugin_name] = plugin_instance
            self.plugin_configs[plugin_name] = config or PluginConfig()
            
            self.logger.info(f"Plugin {plugin_name} loaded successfully")
            return True
            
        except Exception as e:
            self.logger.error(f"Failed to load plugin {plugin_name}: {e}")
            self.logger.debug(traceback.format_exc())
            return False
    
    def _load_entry_point_module(self, plugin_entry: PluginEntry):
        """通过entry_point加载模块"""
        try:
            return importlib.import_module(plugin_entry.module_name)
        except Exception as e:
            self.logger.error(f"Failed to load entry_point module {plugin_entry.module_name}: {e}")
            return None
    
    def _load_file_module(self, plugin_entry: PluginEntry):
        """通过文件路径加载模块"""
        try:
            if os.path.isfile(plugin_entry.file_path):
                # 单文件插件
                spec = importlib.util.spec_from_file_location(
                    plugin_entry.module_name, 
                    plugin_entry.file_path
                )
                module = importlib.util.module_from_spec(spec)
                spec.loader.exec_module(module)
                return module
                
            elif os.path.isdir(plugin_entry.file_path):
                # 包插件
                if plugin_entry.file_path not in sys.path:
                    sys.path.insert(0, os.path.dirname(plugin_entry.file_path))
                
                return importlib.import_module(plugin_entry.module_name)
            
            return None
            
        except Exception as e:
            self.logger.error(f"Failed to load file module {plugin_entry.file_path}: {e}")
            return None
    
    def load_all_plugins(self, configs: Dict[str, PluginConfig] = None) -> Dict[str, bool]:
        """
        加载所有发现的插件
        
        Args:
            configs: 插件配置字典
            
        Returns:
            Dict[str, bool]: 插件名称到加载结果的映射
        """
        results = {}
        configs = configs or {}
        
        for plugin_name in self.discovered_plugins:
            config = configs.get(plugin_name)
            results[plugin_name] = self.load_plugin(plugin_name, config)
        
        loaded_count = sum(results.values())
        self.logger.info(f"Loaded {loaded_count}/{len(results)} plugins")
        
        return results
    
    def unload_plugin(self, plugin_name: str) -> bool:
        """
        卸载插件
        
        Args:
            plugin_name: 插件名称
            
        Returns:
            bool: 卸载是否成功
        """
        if plugin_name not in self.loaded_plugins:
            self.logger.warning(f"Plugin {plugin_name} not loaded")
            return True
        
        try:
            plugin = self.loaded_plugins[plugin_name]
            
            # 清理插件
            plugin.cleanup()
            
            # 移除引用
            del self.loaded_plugins[plugin_name]
            del self.plugin_configs[plugin_name]
            
            if plugin_name in self.discovered_plugins:
                self.discovered_plugins[plugin_name].loaded = False
                self.discovered_plugins[plugin_name].instance = None
            
            self.logger.info(f"Plugin {plugin_name} unloaded successfully")
            return True
            
        except Exception as e:
            self.logger.error(f"Failed to unload plugin {plugin_name}: {e}")
            return False
    
    def get_plugin(self, plugin_name: str) -> Optional[BasePlugin]:
        """获取已加载的插件实例"""
        return self.loaded_plugins.get(plugin_name)
    
    def list_discovered_plugins(self) -> Dict[str, Dict[str, Any]]:
        """列出所有发现的插件"""
        result = {}
        
        for name, entry in self.discovered_plugins.items():
            result[name] = {
                "name": entry.name,
                "module_name": entry.module_name,
                "class_name": entry.class_name,
                "file_path": entry.file_path,
                "entry_point": entry.entry_point,
                "loaded": entry.loaded,
                "checksum": entry.checksum[:16] + "..." if entry.checksum else ""
            }
            
            # 如果已加载，添加元数据信息
            if entry.loaded and entry.instance:
                metadata = entry.instance.metadata
                result[name].update({
                    "version": metadata.version,
                    "description": metadata.description,
                    "author": metadata.author,
                    "type": metadata.plugin_type.value,
                    "requires_root": metadata.requires_root,
                    "supported_platforms": metadata.supported_platforms
                })
        
        return result
    
    def list_loaded_plugins(self) -> Dict[str, BasePlugin]:
        """列出所有已加载的插件"""
        return self.loaded_plugins.copy()
    
    def reload_plugin(self, plugin_name: str) -> bool:
        """
        重新加载插件
        
        Args:
            plugin_name: 插件名称
            
        Returns:
            bool: 重新加载是否成功
        """
        if plugin_name in self.loaded_plugins:
            config = self.plugin_configs.get(plugin_name)
            if not self.unload_plugin(plugin_name):
                return False
        
        # 重新发现插件（以防文件有更新）
        self.discover_plugins()
        
        return self.load_plugin(plugin_name, config)
    
    def cleanup_all_plugins(self):
        """清理所有已加载的插件"""
        for plugin_name in list(self.loaded_plugins.keys()):
            self.unload_plugin(plugin_name)

# 使用示例
if __name__ == "__main__":
    print("=== 插件加载器测试 ===")
    
    # 创建插件加载器
    loader = PluginLoader()
    
    # 发现插件
    discovered = loader.discover_plugins()
    print(f"\n发现的插件: {len(discovered)}")
    for name, entry in discovered.items():
        print(f"- {name}: {entry.class_name} ({entry.file_path})")
    
    # 加载所有插件
    print("\n加载插件...")
    results = loader.load_all_plugins()
    for name, success in results.items():
        status = "成功" if success else "失败"
        print(f"- {name}: {status}")
    
    # 列出已加载的插件
    loaded = loader.list_loaded_plugins()
    print(f"\n已加载的插件: {len(loaded)}")
    for name, plugin in loaded.items():
        print(f"- {name}: {plugin.metadata.description}")
    
    # 清理
    loader.cleanup_all_plugins()